﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.SharePoint;


namespace Amdera.SPMetadata
{
    public class Site : MDData
    {
        public Site(string siteid)
        {
            siteID = siteid;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="webID">GUID-string or site-relative URL</param>
        /// <returns></returns>
        public Web Web(string webID)
        {
            return new Web(siteID, webID);
        }

        /// <summary>
        /// Return SPSite
        /// </summary>
        /// <returns></returns>
        public SPSite This()
        {
            SPSite res = null;
            try
            {
                res = (IsGUID(siteID)) ? (new SPSite(new Guid(siteID))) : (new SPSite(siteID));
            }
            catch { }
            if (res != null)
                res.Dispose();
            return res;
        }
    }

    public class Web : MDData
    {
        public Web(string siteid, string webid)
        {
            siteID = siteid;
            webID = webid;
        }

        void This(SPWeb web, ref object res)
        {
            res = web;
        }

        /// <summary>
        /// Return SPWeb
        /// </summary>
        /// <returns></returns>
        public SPWeb This()
        {
            object res = null;
            Carcass(siteID, webID, This, ref res);
            return (res != null) ? ((SPWeb)res) : (null);
        }

        /// <summary>
        /// Return available SPField
        /// </summary>
        /// <param name="fieldID">GUID-string or Internal name (Sensitivity to the register) or Display name (Sensitivity to the register)</param>
        /// <returns></returns>
        public Field Field(string fieldID)
        {
            return new WebField(siteID, webID, fieldID, true);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fieldID">GUID-string or Internal name or Display name</param>
        /// <returns></returns>
        public Field NotAvailableField(string fieldID)
        {
            return new WebField(siteID, webID, fieldID, false);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="listID">GUID-string or site-relative URL for a list or Title</param>
        /// <returns></returns>
        public List List(string listID)
        {
            return new List(siteID, webID, listID);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="contentTypeID">SPContentTypeId or Name</param>
        /// <returns></returns>
        public ContentType ContentType(string contentTypeID)
        {
            return new ContentType(siteID, webID, contentTypeID, true);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="contentTypeID">SPContentTypeId or Name</param>
        /// <returns></returns>
        public ContentType NotAvailableContentType(string contentTypeID)
        {
            return new ContentType(siteID, webID, contentTypeID, false);
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="listDefinitionID">FeatureId or Internal name or Name or Type</param>
        /// <returns></returns>
        public ListTemplate ListTemplate(string listDefinitionID)
        {
            return new ListTemplate(siteID, webID, listDefinitionID);
        }
    }

    public class List : MDData
    {
        protected string listID;
        public List(string siteid, string webid, string listid)
        {
            siteID = siteid;
            webID = webid;
            listID = listid;
        }

        void This(SPWeb web, ref object res)
        {
            res = null;
            SPList liCurrent = GetList(web, listID);
            if (liCurrent != null)
                res = liCurrent;
        }

        /// <summary>
        /// Return SPList
        /// </summary>
        /// <returns></returns>
        public SPList This()
        {
            object res = null;
            Carcass(siteID, webID, This, ref res);
            return (res != null) ? ((SPList)res) : (null);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fieldID">GUID-string or Internal name or Display name</param>
        /// <returns></returns>
        public Field Field(string fieldID)
        {
            return new ListField(siteID, webID, listID, fieldID);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="viewID">GUID-string or URL or Title</param>
        /// <returns></returns>
        public View View(string viewID)
        {
            return new View(siteID, webID, listID, viewID);
        }
    }

    public class Field : MDData
    {
        protected string fieldID;

        public Field(string siteID, string webID, string fieldID)
        {
            this.siteID = siteID;
            this.webID = webID;
            this.fieldID = fieldID;
        }

        /// <summary>
        /// Return SPField
        /// </summary>
        /// <returns></returns>
        public virtual SPField This() { return null; }

        /// <summary>
        /// Return SPField
        /// </summary>
        /// <returns></returns>
        public virtual SPField SPThis() { return null; }
    }

    public class WebField : Field
    {
        protected bool available;

        public WebField(string siteID, string webID, string fieldID, bool availableFields)
            : base(siteID, webID, fieldID)
        {
            this.available = availableFields;
        }

        void This(SPWeb web, ref object res)
        {
            res = GetField((available) ? (web.AvailableFields) : (web.Fields), fieldID);
        }

        public override SPField This()
        {
            object res = null;
            Carcass(siteID, webID, This, ref res);
            return (res != null) ? ((SPField)res) : (null);
        }

        void SPThis(SPWeb web, ref object res)
        {
            res = GetFieldTrue((available) ? (web.AvailableFields) : (web.Fields), fieldID);
        }

        public override SPField SPThis()
        {
            object res = null;
            Carcass(siteID, webID, SPThis, ref res);
            return (res != null) ? ((SPField)res) : (null);
        }
    }

    public class ListField : Field
    {
        protected string listID;
        public ListField(string siteID, string webID, string listID, string fieldID)
            : base(siteID, webID, fieldID)
        {
            this.listID = listID;
        }

        void This(SPWeb web, ref object res)
        {
            SPList list = GetList(web, listID);
            if (list != null)
                res = GetField(list.Fields, fieldID);
        }

        public override SPField This()
        {
            object res = null;
            Carcass(siteID, webID, This, ref res);
            return (res != null) ? ((SPField)res) : (null);
        }

        void SPThis(SPWeb web, ref object res)
        {
            SPList list = GetList(web, listID);
            if (list != null)
                res = GetFieldTrue(list.Fields, fieldID);
        }

        public override SPField SPThis()
        {
            object res = null;
            Carcass(siteID, webID, SPThis, ref res);
            return (res != null) ? ((SPField)res) : (null);
        }
    }

    public class ContentTypeField : Field
    {
        protected string contentTypeID;
        protected bool available;

        public ContentTypeField(string siteID, string webID, string contentTypeID, string fieldID, bool availableContentType)
            : base(siteID, webID, fieldID)
        {
            this.contentTypeID = contentTypeID;
            this.available = availableContentType;
        }

        void This(SPWeb web, ref object res)
        {
            SPContentType ctCurrent = GetContentType(web, contentTypeID, available);
            if (ctCurrent != null)
                res = GetField(ctCurrent.Fields, fieldID);
        }

        public override SPField This()
        {
            object res = null;
            Carcass(siteID, webID, This, ref res);
            return (res != null) ? ((SPField)res) : (null);
        }

        void SPThis(SPWeb web, ref object res)
        {
            SPContentType ctCurrent = GetContentType(web, contentTypeID, available);
            if (ctCurrent != null)
                res = GetFieldTrue(ctCurrent.Fields, fieldID);
        }

        public override SPField SPThis()
        {
            object res = null;
            Carcass(siteID, webID, SPThis, ref res);
            return (res != null) ? ((SPField)res) : (null);
        }
    }

    public class View : MDData
    {
        protected string listID;
        protected string viewID;

        public View(string siteID, string webID, string listID, string viewID)
        {
            this.siteID = siteID;
            this.webID = webID;
            this.listID = listID;
            this.viewID = viewID;
        }

        void SPThis(SPWeb web, ref object res)
        {
            res = GetViewTrue(web, listID, viewID);
        }

        /// <summary>
        /// Return SPView
        /// </summary>
        /// <returns></returns>
        public SPView SPThis()
        {
            object res = null;
            Carcass(siteID, webID, SPThis, ref res);
            return (res != null) ? ((SPView)res) : (null);
        }

        void This(SPWeb web, ref object res)
        {
            res = GetView(web, listID, viewID);
        }

        /// <summary>
        /// Return SPView
        /// </summary>
        /// <returns></returns>
        public SPView This()
        {
            object res = null;
            Carcass(siteID, webID, This, ref res);
            return (res != null) ? ((SPView)res) : (null);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fieldID">GUID-string or Internal name or Display name of List, and Internal name of field reference</param>
        /// <returns></returns>
        public ViewField Field(string fieldID)
        {
            return new ViewField(siteID, webID, listID, viewID, fieldID);
        }
    }

    public class ViewField : Field
    {
        protected string listID;
        protected string viewID;

        public ViewField(string siteID, string webID, string listID, string viewID, string fieldID)
            : base(siteID, webID, fieldID)
        {
            this.listID = listID;
            this.viewID = viewID;
        }

        void This(SPWeb web, ref object res)
        {
            SPView view = GetView(web, listID, viewID);
            if (view != null)
            {
                ListField lfCurrent = new ListField(siteID, webID, listID, fieldID);
                SPField fldCurrent = lfCurrent.This();
                if (fldCurrent != null)
                {
                    if (view.ViewFields.Exists(fldCurrent.InternalName))
                        res = fldCurrent;
                }
                else
                {
                    if (IsGUID(fieldID))
                    {
                        try
                        {
                            res = view.ParentList.ParentWeb.AvailableFields[new Guid(fieldID)];
                        }
                        catch { }
                    }
                    else
                    {
                        if (view.ParentList.ParentWeb.AvailableFields.OfType<SPField>().Count(fld => fld.InternalName.ToUpper() == fieldID.ToUpper()) > 0)
                            res = view.ParentList.ParentWeb.AvailableFields.OfType<SPField>().First(fld => fld.InternalName.ToUpper() == fieldID.ToUpper());
                    }
                }
            }
        }

        public override SPField This()
        {
            object res = null;
            Carcass(siteID, webID, This, ref res);
            return (res != null) ? ((SPField)res) : (null);
        }

        void SPThis(SPWeb web, ref object res)
        {
            SPView view = GetViewTrue(web, listID, viewID);
            if (view != null)
            {
                ListField lfCurrent = new ListField(siteID, webID, listID, fieldID);
                SPField fldCurrent = lfCurrent.SPThis();
                if (fldCurrent != null)
                {
                    if (view.ViewFields.Exists(fldCurrent.InternalName))
                        res = fldCurrent;
                }
                else
                {
                    if (IsGUID(fieldID))
                    {
                        try
                        {
                            res = view.ParentList.ParentWeb.AvailableFields[new Guid(fieldID)];
                        }
                        catch { }
                    }
                    else
                    {
                        try
                        {
                            res = view.ParentList.ParentWeb.AvailableFields.GetFieldByInternalName(fieldID);
                        }
                        catch { }
                    }
                }
            }
        }

        public override SPField SPThis()
        {
            object res = null;
            Carcass(siteID, webID, SPThis, ref res);
            return (res != null) ? ((SPField)res) : (null);
        }
    }

    public class ContentType : MDData
    {
        string contentTypeID;
        bool available;

        public ContentType(string siteID, string webID, string contentTypeID, bool availableContentTypes)
        {
            this.siteID = siteID;
            this.webID = webID;
            this.contentTypeID = contentTypeID;
            this.available = availableContentTypes;
        }

        void This(SPWeb web, ref object res)
        {
            res = null;
            SPContentType ctCurrent = GetContentType(web, contentTypeID, available);
            if (ctCurrent != null)
                res = ctCurrent;
        }

        /// <summary>
        /// Return SPContentType
        /// </summary>
        /// <returns></returns>
        public SPContentType This()
        {
            object res = null;
            Carcass(siteID, webID, This, ref res);
            return (res != null) ? ((SPContentType)res) : (null);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fieldID">GUID-string or Internal name or Display name</param>
        /// <returns></returns>
        public Field Field(string fieldID)
        {
            return new ContentTypeField(siteID, webID, contentTypeID, fieldID, available);
        }
    }

    public class ListTemplate : MDData
    {
        protected string listDefinitionID;

        public ListTemplate(string siteID, string webID, string listDefinitionID)
        {
            this.siteID = siteID;
            this.webID = webID;
            this.listDefinitionID = listDefinitionID;
        }

        void This(SPWeb web, ref object res)
        {
            res = null;
            if (IsGUID(listDefinitionID))
            {
                if (web.ListTemplates.OfType<SPListTemplate>().Count(lt => listDefinitionID.ToUpper().Contains(lt.FeatureId.ToString().ToUpper())) > 0)
                    res = web.ListTemplates.OfType<SPListTemplate>().First(lt => listDefinitionID.ToUpper().Contains(lt.FeatureId.ToString().ToUpper()));
            }
            else
            {
                if (web.ListTemplates.OfType<SPListTemplate>().Count(lt => lt.Type.ToString().ToUpper() == listDefinitionID.ToUpper()) > 0)
                    res = web.ListTemplates.OfType<SPListTemplate>().First(lt => lt.Type.ToString().ToUpper() == listDefinitionID.ToUpper());
                if (res == null)
                {
                    if (web.ListTemplates.OfType<SPListTemplate>().Count(lt => lt.InternalName.ToString().ToUpper() == listDefinitionID.ToUpper()) > 0)
                        res = web.ListTemplates.OfType<SPListTemplate>().First(lt => lt.InternalName.ToString().ToUpper() == listDefinitionID.ToUpper());
                    if (res == null)
                    {
                        /*try
                        {
                            res = web.ListTemplates[listDefinitionID];
                        }
                        catch { }*/
                        if (web.ListTemplates.OfType<SPListTemplate>().Count(lt => lt.Name.ToString().ToUpper() == listDefinitionID.ToUpper()) > 0)
                            res = web.ListTemplates.OfType<SPListTemplate>().First(lt => lt.Name.ToString().ToUpper() == listDefinitionID.ToUpper());
                    }
                }
            }
        }

        /// <summary>
        /// Return SPListTemplate
        /// </summary>
        /// <returns></returns>
        public SPListTemplate This()
        {
            object res = null;
            Carcass(siteID, webID, This, ref res);
            return (res != null) ? ((SPListTemplate)res) : (null);
        }
    }

    public class MDData
    {
        protected string webID;
        protected string siteID;
        protected delegate void WebMethod(SPWeb web, ref object res);

        protected SPContentType GetContentType(SPWeb web, string contentTypeID, bool available)
        {
            SPContentType res = null;
            try
            {
                SPContentTypeCollection ctCollection = (available) ? (web.AvailableContentTypes) : (web.ContentTypes);
                if (contentTypeID.ToLower().StartsWith("0x"))
                {
                    if (ctCollection.OfType<SPContentType>().Count(ct => ct.Id.ToString().ToUpper() == contentTypeID.ToUpper()) > 0)
                        res = ctCollection.OfType<SPContentType>().First(ct => ct.Id.ToString().ToUpper() == contentTypeID.ToUpper());
                }
                else
                {
                    if (ctCollection.OfType<SPContentType>().Count(ct => ct.Name.ToUpper() == contentTypeID.ToUpper()) > 0)
                        res = ctCollection.OfType<SPContentType>().First(ct => ct.Name.ToUpper() == contentTypeID.ToUpper());
                }
            }
            catch { }
            return res;
        }

        protected bool IsGUID(string expression)
        {
            if (expression != null)
            {
                Regex guidRegEx = new Regex(@"^(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}$", RegexOptions.Compiled);
                return guidRegEx.IsMatch(expression);
            }
            return false;
        }

        protected SPList GetList(SPWeb web, string listID)
        {
            SPList res = null;
            if (IsGUID(listID))
            {
                try { res = web.Lists[new Guid(listID)]; }
                catch { }
            }
            else
            {
                try
                {
                    res = web.GetList((web.ServerRelativeUrl + listID).Replace("//", "/"));
                }
                catch { }

                if (res == null)
                {
                    try { res = web.Lists[listID]; }
                    catch { }
                }
            }
            return res;
        }

        protected SPView GetView(SPWeb web, string listID, string viewID)
        {
            SPView res = null;
            SPList liCurrent = GetList(web, listID);
            if (liCurrent != null)
            {
                if (IsGUID(viewID))
                {
                    try
                    {
                        res = liCurrent.Views[new Guid(viewID)];
                    }
                    catch { }
                }
                else
                {
                    if (viewID.ToLower().EndsWith(".aspx"))
                    {
                        if (liCurrent.Views.OfType<SPView>().Count(v => v.Url.ToUpper() == viewID.ToUpper()) > 0)
                            res = liCurrent.Views.OfType<SPView>().First(v => v.Url.ToUpper() == viewID.ToUpper());
                    }
                    else
                    {
                        if (liCurrent.Views.OfType<SPView>().Count(v => v.Title.ToUpper() == viewID.ToUpper()) > 0)
                            res = liCurrent.Views.OfType<SPView>().First(v => v.Title.ToUpper() == viewID.ToUpper());
                    }
                }
            }
            return res;
        }

        protected SPView GetViewTrue(SPWeb web, string listID, string viewID)
        {
            SPView res = null;
            SPList liCurrent = GetList(web, listID);
            if (liCurrent != null)
            {
                if (IsGUID(viewID))
                {
                    try
                    {
                        res = liCurrent.Views[new Guid(viewID)];
                    }
                    catch { }
                }
                else
                {
                    if (viewID.ToLower().EndsWith(".aspx"))
                    {
                        if (liCurrent.Views.OfType<SPView>().Count(v => v.Url.ToUpper() == viewID.ToUpper()) > 0)
                            res = liCurrent.Views.OfType<SPView>().First(v => v.Url.ToUpper() == viewID.ToUpper());
                    }
                    else
                    {
                        try
                        {
                            res = liCurrent.Views[viewID];
                        }
                        catch { }
                    }
                }
            }
            return res;
        }

        protected SPField GetField(SPFieldCollection fldCollection, string fieldID)
        {
            SPField res = null;
            if (IsGUID(fieldID))
            {
                try
                {
                    res = fldCollection[new Guid(fieldID)];
                }
                catch { }
            }
            else
            {
                if (fldCollection.OfType<SPField>().Count(fld => (!fld.Hidden) && (fld.InternalName.ToUpper() == fieldID.ToUpper())) > 0)
                    res = fldCollection.OfType<SPField>().First(fld => (!fld.Hidden) && (fld.InternalName.ToUpper() == fieldID.ToUpper()));
                if (res == null)
                {
                    if (fldCollection.OfType<SPField>().Count(fld => (!fld.Hidden) && (fld.Title.ToUpper() == fieldID.ToUpper())) > 0)
                        res = fldCollection.OfType<SPField>().First(fld => (!fld.Hidden) && (fld.Title.ToUpper() == fieldID.ToUpper()));
                }
            }
            return res;
        }

        protected SPField GetFieldTrue(SPFieldCollection fldCollection, string fieldID)
        {
            SPField res = null;
            if (IsGUID(fieldID))
            {
                try
                {
                    res = fldCollection[new Guid(fieldID)];
                }
                catch { }
            }
            else
            {
                if (fldCollection.ContainsField(fieldID))
                {
                    try
                    {
                        res = fldCollection.GetFieldByInternalName(fieldID);
                    }
                    catch { }
                    if (res == null)
                    {
                        try
                        {
                            res = fldCollection[fieldID];
                        }
                        catch { }
                    }
                }
            }
            return res;
        }

        string webServerRelativeUrl(string site, string web)
        {
            string res = "";
            site = site.ToUpper();
            web = web.ToUpper();
            if (web.StartsWith("/"))
                res = web;
            else
            {
                if (web.StartsWith("HTTP://"))
                    res = web.Substring(site.Length);
                else
                    res = "";
            }
            return res;
        }

        protected void Carcass(string siteID, string webID, WebMethod webMethod, ref object res)
        {
            SPSite site = null;
            try
            {
                site = (IsGUID(siteID)) ? (new SPSite(new Guid(siteID))) : (new SPSite(siteID));
            }
            catch { }
            if (site != null)
            {
                SPWeb web = null;
                if (IsGUID(webID))
                {
                    try
                    {
                        web = site.AllWebs[new Guid(webID)];
                    }
                    catch
                    {

                    }
                }
                else
                {
                    string strWebServerRelativeUrl = webServerRelativeUrl(site.Url, webID);
                    if (strWebServerRelativeUrl != String.Empty)
                    {
                        if (site.AllWebs.OfType<SPWeb>().Count(w => w.ServerRelativeUrl.ToUpper() == strWebServerRelativeUrl.ToUpper()) > 0)
                            web = site.AllWebs.OfType<SPWeb>().First(w => w.ServerRelativeUrl.ToUpper() == strWebServerRelativeUrl.ToUpper());
                    }
                }
                if (web != null)
                {
                    webMethod(web, ref res);
                    web.Dispose();
                }
                site.Dispose();
            }
        }
    }

    public static class SPMetadata
    {
        /// <param name="siteID">ID or URL</param>
        public static Site Site(string siteID)
        {
            return new Site(siteID);
        }
    }
}
